//
//  XCCircleSpinButton.swift
//  Pods
//
//  Created by edy on 2023/3/1.
//

import Foundation
import UIKit

public class XCCircleSpinButton: UIButton {
    // MARK: - Properties
    /// Button默认以及选择的文案
    internal var btnNormalTitle: String?
    internal var btnSelectedTitle: String?
    /// Button默认以及选择的文案（富文本类型）
    internal var btnAttributedNormalTitle: NSAttributedString?
    internal var btnAttributedSelectedTitle: NSAttributedString?
    /// Button背景图片
    internal var btnBackgroundNormalImage: UIImage?
    internal var btnBackgroundSelectedImage: UIImage?
    /// Button图片
    internal var btnNormalImage: UIImage?
    internal var btnSelectedImage: UIImage?
    
    fileprivate var animationDuration: CFTimeInterval = 0.1
    
    fileprivate var isAnimating: Bool = false
        
    fileprivate var btnWidth: CGFloat?
    fileprivate var btnHeight: CGFloat?
    
    /// Sets the button corner radius
    @IBInspectable var cornrRadius: CGFloat = 0 {
        willSet {
            layer.cornerRadius = newValue
        }
    }
    
    /// Sets the spinner color
    public var spinnerColor: UIColor = UIColor.gray
    
    var spinnerSize: UInt?
    /// Sets the button title for its normal state
    
    public var normalTitle: String? {
        get {return self.title(for: .normal)}
        set {self.setTitle(newValue, for: .normal)}
    }
    
    /// Sets the button title for its selected state
    public var selectedTitle: String? {
        get {return self.title(for: .selected)}
        set {self.setTitle(newValue, for: .selected)}
    }
    
    /// Sets the button attributed title for its normal state
    public var attributedNormalTitle: NSAttributedString? {
        get {return self.attributedTitle(for: .normal)
        } set {self.setAttributedTitle(newValue, for: .normal)
        }
    }
    
    /// Sets the button attributed title for its normal state
    public var attributedSelectedTitle: NSAttributedString? {
        get {return self.attributedTitle(for: .selected)
        } set {self.setAttributedTitle(newValue, for: .selected)
        }
    }
    
    // MARK: - Initializers
    public required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        setUp()
    }
    
    public override init(frame: CGRect) {
        super.init(frame: frame)
        setUp()
    }

    /// init method
    ///
    /// - Parameter title: button title for normal state
    public init(title: String) {
        super.init(frame: .zero)
        setTitle(title, for: .normal)
        setUp()
    }
    
    open override func layoutSubviews() {
        super.layoutSubviews()
    }

    func setUp() {
        
        self.removeAnimationLayer()
        if self.cornrRadius == 0 {
            self.cornrRadius = self.layer.cornerRadius
        }
        if layer.sublayers != nil {
            
            for item in layer.sublayers! where item is CAGradientLayer {
                
                item.cornerRadius = self.cornrRadius
                item.masksToBounds = true
            }
        }
        self.layer.cornerRadius = self.cornrRadius
        self.layer.masksToBounds = true
        
        if self.image(for: .normal) != nil && self.normalTitle != nil {
            let spacing: CGFloat = 10
            // the amount of spacing to appear between image and title\
            self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: spacing)
            self.titleEdgeInsets = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: 0)
        }
        
    }
    
    public func startAnimate(spinnercolor: UIColor = .gray, spinnerSize: UInt?, complete: (() -> Void)?) {
        if self.cornrRadius == 0 {
            self.cornrRadius = self.layer.cornerRadius
        }
        self.removeAnimationLayer()
        isAnimating = true
        self.spinnerColor = spinnercolor
        self.spinnerSize = spinnerSize
        
        self.layer.cornerRadius = self.frame.height / 2
        if layer.sublayers != nil {
            for item in layer.sublayers! where item is CAGradientLayer {
                item.cornerRadius = self.frame.height / 2
            }
        }
        self.collapseAnimation(complete: complete)
        
    }
    
    public func stopAnimate(complete: (() -> Void)?) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
            print("1111111")
            self.backToDefaults(setToDefaults: false, complete: complete)
        })
    }
    
    func removeAnimationLayer() {
        if layer.sublayers != nil {
            for item in layer.sublayers! where item is CAShapeLayer {
                item.removeAllAnimations()
                item.removeFromSuperlayer()
            }
        }
    }
    
    /// collapse animation
    func collapseAnimation(complete: (() -> Void)?) {
        
        btnNormalTitle = normalTitle
        normalTitle = nil
        
        btnSelectedTitle = selectedTitle
        selectedTitle = nil
        
        btnAttributedNormalTitle = attributedNormalTitle
        attributedNormalTitle = nil
        
        btnAttributedSelectedTitle = attributedSelectedTitle
        attributedSelectedTitle = nil
    
        
        btnBackgroundNormalImage = self.backgroundImage(for: .normal)
        btnBackgroundSelectedImage = self.backgroundImage(for: .selected)
        
        btnNormalImage = self.image(for: .normal)
        btnSelectedImage = self.image(for: .selected)
        
        btnWidth = frame.width
        btnHeight = frame.height
        
        self.setImage(nil, for: .normal)
        self.setImage(nil, for: .selected)
        isUserInteractionEnabled = false
        
        let animaton = CABasicAnimation(keyPath: "bounds.size.width")
        animaton.fromValue = bounds.width
        animaton.toValue =  bounds.height
        animaton.duration = animationDuration
        animaton.fillMode = CAMediaTimingFillMode.both
        animaton.isRemovedOnCompletion = false
        if layer.sublayers != nil {
            
            for item in layer.sublayers! where item is CAGradientLayer {
                item.add(animaton, forKey: animaton.keyPath)
            }
        }
        layer.add(animaton, forKey: animaton.keyPath)
        self.perform(#selector(startSpinner), with: nil, afterDelay: animationDuration)
        
        DispatchQueue.main.asyncAfter(deadline: .now() + animationDuration, execute: {
            if complete != nil {
                complete!()
            }
        })
        
    }
        
    /// set back to default state of button
    ///
    /// - Parameters:
    ///   - completionType: completion type
    ///   - setToDefaults: back to default state
    ///   - complete: complation block (call after animation Stop)
    func backToDefaults(setToDefaults: Bool, complete: (() -> Void)?) {
        if !isAnimating {
            return
        }
        self.removeAnimationLayer()
        self.setDefaultDataToButton(complete: complete)
        self.isUserInteractionEnabled = true
    }
    
    /// set default state
    ///
    /// - Parameter complete: complation block (call after animation Stop)
    func setDefaultDataToButton(complete: (() -> Void)?) {
        
        
        self.removeAnimationLayer()
        self.setTitle(self.btnNormalTitle, for: .normal)
        self.setTitle(self.btnSelectedTitle, for: .selected)
        
        if self.btnAttributedNormalTitle != nil {
            self.setAttributedTitle(self.btnAttributedNormalTitle, for: .normal)
        }
        
        if self.btnAttributedSelectedTitle != nil {
            self.setAttributedTitle(self.btnAttributedSelectedTitle, for: .selected)
        }

        
        self.setBackgroundImage(self.btnBackgroundNormalImage, for: .normal)
        self.setBackgroundImage(self.btnBackgroundSelectedImage, for: .selected)
        self.setImage(self.btnNormalImage, for: .normal)
        self.setImage(self.btnSelectedImage, for: .selected)
        self.isUserInteractionEnabled = true
        
        let animation = CABasicAnimation(keyPath: "bounds.size.width")
        animation.fromValue = btnHeight
        animation.toValue = btnWidth
        animation.duration = self.animationDuration
        animation.fillMode = CAMediaTimingFillMode.forwards
        animation.isRemovedOnCompletion = false
        
        self.layer.add(animation, forKey: animation.keyPath)
        if self.layer.sublayers != nil {
            
            for item in self.layer.sublayers! where item is CAGradientLayer {
                item.add(animation, forKey: animation.keyPath)
                item.cornerRadius = self.cornrRadius
            }
        }
        self.isAnimating = false
        self.layer.cornerRadius = self.cornrRadius
        if complete != nil {
            complete!()
            }
        
    }
    
    /// start spinner
    @objc func startSpinner() {
        XCCircleSpin().setupSpinnerAnimation(layer: self.layer, frame: self.bounds, color: self.spinnerColor, spinnerSize: self.spinnerSize)
    }
}

 
 
class XCCircleSpin {
    
    /// setup spinner layer
    ///
    /// - Parameters:
    ///   - layer: layer Parent layer (Button layer)
    ///   - frame: frame of parant layer
    ///   - color: color of spinner
    ///   - spinnerSize: size of spinner layer
    func setupSpinnerAnimation(layer: CALayer, frame: CGRect, color: UIColor, spinnerSize: UInt?) {
        
        let defaultPadding: CGFloat = 10.0
        var center = CGPoint(x: defaultPadding / 2, y: defaultPadding / 2)
        var sizeValue = max(min(frame.width, frame.height) - defaultPadding, 1.0)
        if spinnerSize != nil && CGFloat(spinnerSize!) < sizeValue {
            sizeValue =  max(CGFloat(spinnerSize!), 1.0)
            center = CGPoint(x: frame.height / 2 - sizeValue / 2, y: frame.height / 2 - sizeValue / 2)
        }
        let beginTime: Double = 0.5
        let strokeStartDuration: Double = 1.2
        let strokeEndDuration: Double = 0.7
        
        let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")
        rotationAnimation.byValue = Float.pi * 2
        rotationAnimation.timingFunction = CAMediaTimingFunction(name: .linear)
        
        let strokeEndAnimation = CABasicAnimation(keyPath: "strokeEnd")
        strokeEndAnimation.duration = strokeEndDuration
        strokeEndAnimation.timingFunction = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 0.2, 1.0)
        strokeEndAnimation.fromValue = 0
        strokeEndAnimation.toValue = 1
        
        let strokeStartAnimation = CABasicAnimation(keyPath: "strokeStart")
        strokeStartAnimation.duration = strokeStartDuration
        strokeStartAnimation.timingFunction = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 0.2, 1.0)
        strokeStartAnimation.fromValue = 0
        strokeStartAnimation.toValue = 1
        strokeStartAnimation.beginTime = beginTime
        
        let groupAnimation = CAAnimationGroup()
        groupAnimation.animations = [rotationAnimation, strokeEndAnimation, strokeStartAnimation]
        groupAnimation.duration = strokeStartDuration + beginTime
        groupAnimation.repeatCount = .infinity
        groupAnimation.isRemovedOnCompletion = false
        groupAnimation.fillMode = .forwards
        
        let circle = XCCircleSpinShape.stroke.layerWith(size: CGSize(width: sizeValue, height: sizeValue), color: color)
        let frame = CGRect(
            x: center.x,
            y: center.y,
            width: sizeValue,
            height: sizeValue
        )
        
        circle.frame = frame
        circle.add(groupAnimation, forKey: "animation")
        layer.addSublayer(circle)
    }
    
}



enum XCCircleSpinShape {
    case ring
    case circle
    case line
    case stroke
    case pacman
    
    /// Return CALayer of specific shape
    ///
    /// - Parameters:
    ///   - size: size of draw shape area
    ///   - color: color of shape
    /// - Returns: CAlayer of shape
    func layerWith(size: CGSize, color: UIColor) -> CALayer {
        let layer: CAShapeLayer = CAShapeLayer()
        var path: UIBezierPath = UIBezierPath()
        
        switch self {
        case .pacman:
            path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2),
                        radius: size.width / 4,
                        startAngle: 0,
                        endAngle: CGFloat(2 * Double.pi),
                        clockwise: true)
            layer.fillColor = nil
            layer.strokeColor = color.cgColor
            layer.lineWidth = size.width / 2
        case .circle:
            path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2),
                        radius: (size.width / 2),
                        startAngle: 0,
                        endAngle: CGFloat(2 * Double.pi),
                        clockwise: false)
            layer.fillColor = color.cgColor
            
        case .ring:
            path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2),
                        radius: (size.width / 2),
                        startAngle: CGFloat(-3 * Double.pi / 4),
                        endAngle: CGFloat(-Double.pi / 4),
                        clockwise: false)
            layer.fillColor = nil
            layer.strokeColor = color.cgColor
            layer.lineWidth = 2
            
        case .line:
            path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: size.width, height: size.height),
                                cornerRadius: size.width / 2)
            layer.fillColor = color.cgColor
            
        case .stroke:
            path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2),
                        radius: size.width / 2,
                        startAngle: -(.pi / 2),
                        endAngle: .pi + .pi / 2,
                        clockwise: true)
            layer.fillColor = nil
            layer.strokeColor = color.cgColor
            layer.lineWidth = 2
        }
        layer.backgroundColor = nil
        layer.path = path.cgPath
        layer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        
        return layer
    }

}


extension UIImage {

    public static func xc_image(with color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) -> UIImage? {
        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        UIGraphicsBeginImageContext(rect.size)
        guard let context = UIGraphicsGetCurrentContext() else {
            return nil
        }
        context.setFillColor(color.cgColor)
        context.fill(rect)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
}


extension UIButton {
    
    public func xc_setBackgroundColor(color: UIColor, for state: UIControl.State) {
        let image = UIImage.xc_image(with: color)
        self.setBackgroundImage(image, for: state)
        if state == .selected {
            self.setBackgroundImage(image, for: [.selected, .highlighted])
        }
    }
}


/* 案例代码
 // 上下班按钮
 lazy var workBtn: XCCircleSpinButton = {
     let workB = XCCircleSpinButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
     workB.frame = CGRect(x: 100, y:100, width: 176, height: 50)
     workB.xc_setBackgroundColor(color: .red, for: .normal)
     workB.setTitle("online_status", for: .normal)
     workB.setTitleColor(.white, for: .normal)
     
     workB.xc_setBackgroundColor(color: .blue, for: .selected)
     workB.setTitle("offline_status", for: .selected)
     workB.setTitleColor(.white, for: .selected)
     workB.layer.cornerRadius = 15
     workB.layer.masksToBounds = true
     return workB
 }()
 
 private let group = DispatchGroup()

 override func viewDidLoad() {
     super.viewDidLoad()
     self.workBtn.addTarget(self, action: #selector(workAction), for: .touchUpInside)
     self.view.addSubview(self.workBtn)
 }
 
 @objc func workAction() {
     self.workBtn.startAnimate(spinnercolor: .white, spinnerSize: 20, complete: nil)
     self.perform(#selector(showMatcherCount), with: nil, afterDelay: 4, inModes: [RunLoopMode.commonModes])
 }
 
 @objc func showMatcherCount() {
     print("ccccccc")
     self.workBtn.stopAnimate {
         print("aaaaa")
         self.workBtn.isSelected = !self.workBtn.isSelected
     }
 }
 */
